package simmar.xcs;

import java.util.Random;

import ch.idsia.agents.Agent;
import ch.idsia.benchmark.mario.environments.Environment;

import simmar.xcs.XCSKnowledge;
import simmar.xcs.XCSObservation;
import simmar.utilities.IO;
import simmar.utilities.Logger;

public class XCSAgent implements Agent, XCSConfig
{
    protected boolean _action[];
    protected String _name;    

    protected int[] _previousCondition;
    protected int[] _currentCondition;
    protected Action _previousAction;

    protected XCS _xcs;
    protected XCSKnowledge _knowledge;

    public XCSAgent()
    {
        _name = "XCSAgent";
        _action = new boolean[Environment.numberOfKeys];
        _xcs = new XCS();
        _knowledge = new XCSKnowledge();
        _previousCondition = null;
        _currentCondition = new int[CONDITION_SIZE];
        _previousAction = XCSConfig.Action.UNDEFINED;        

        reset();
        
//        // Make list of 1000 random ints for seeding test runs
//        Random rand = new Random();
//        int[] seeds = new int[1000];
//        
//        for (int i = 0; i < 1000; i++)
//        {
//            seeds[i] = rand.nextInt(Integer.MAX_VALUE);
//        }
//        
//        IO.SaveObject(seeds, "seeds");
//        
//        int[] loadedSeeds = IO.LoadObject("seeds");
//        
//        for (int i = 0; i < 1000; i++)
//        {
//            Logger.logLine("seeds[" + i + "] = " + seeds[i]);
//        }
//        
//        System.exit(0);
    }

    // // PUBLIC METHODS, IMPLEMENTING AGENT INTERFACE ////

    public boolean[] getAction()
    {
//        System.out.println("---------------------------------------------------------------------------------------------------------");
//        Logger.log("**** GET ACTION ****\n");
//        System.out.println("---------------------------------------------------------------------------------------------------------");

        Action actionName = _xcs.runPerformanceComponent(_currentCondition);        
        _action = MARIO_ACTIONS[actionName.ordinal()].clone();

        _previousCondition = _currentCondition;
        _previousAction = actionName;
        
        if (actionName == Action.RIGHT_JUMP ||
            actionName == Action.RIGHT_JUMP_SPEED) 
        {
            _knowledge.jumpStart();
        }
        
        if (XCS_DEBUG_LEVEL.compareTo(DEBUG_LEVEL.SILENT) > 0)
        {
            Logger.logLine("Input condition: " + conditionToString(_currentCondition));
            Logger.logLine("Selected action: " + actionName.toString());
        }           
        
//            Logger.logLine("Population size = " + _xcs.getPopulationSize() + " / " + _xcs.getPopulationSizeReal());                    
        
        return _action;
    }

    public void integrateObservation(Environment environment)
    {
        // System.out.println("---------------------------------------------------------------------------------------------------------");
        // Logger.log("**** INTEGRATE OBSERVATION ****\n");
        // System.out.println("---------------------------------------------------------------------------------------------------------");
        
        _knowledge.update(environment);

        for (int i = 0; i < CONDITION_SIZE; i++)
            _currentCondition[i] = 0;
        
        _currentCondition[Condition.CAN_ASCEND.ordinal()] = (int)XCSObservation.getBinaryCanAscend(_knowledge, environment);
        _currentCondition[Condition.OBSTACLE_AHEAD.ordinal()] = (int)XCSObservation.getBinaryObstacleAhead(_knowledge, environment);
        _currentCondition[Condition.PIT_AHEAD.ordinal()] = (int)XCSObservation.getBinaryPitAhead(_knowledge, environment);        
//        _currentCondition[Condition.DANGER_BELOW.ordinal()] = (int)InputLibrary.getBinaryDangerBelow(_knowledge, environment);
        _currentCondition[Condition.SHOT_AIMED.ordinal()] = (int)XCSObservation.getBinaryShotAimed(_knowledge, environment);
        _currentCondition[Condition.ENEMY_AHEAD.ordinal()] = (int)XCSObservation.getBinaryEnemyAhead(_knowledge, environment);
        
        if (XCS_DEBUG_LEVEL.compareTo(DEBUG_LEVEL.SILENT) > 0)
            XCSObservation.printNeighborhood(11, environment, ObservationType.MERGED);
    }

    public void giveIntermediateReward(float intermediateReward)
    {        
        // System.out.println("---------------------------------------------------------------------------------------------------------");
        // Logger.log("**** GIVE INTERMEDIATE REWARD ****\n");
        // System.out.println("---------------------------------------------------------------------------------------------------------");
        if (XCS_MODE == MODE.RUN || XCS_MODE == MODE.TEST)
            return;
   
        if (XCS_DEBUG_LEVEL.compareTo(DEBUG_LEVEL.SILENT) > 0)
            Logger.logLine("Reward is: " + intermediateReward);
        
         _xcs.runReinforcementComponent(intermediateReward, true);
         
//       Logger.logLine("Population size = " + _xcs.getPopulationSize() + " / " + _xcs.getPopulationSizeReal());
    }

    public void reset()
    {
        _knowledge.reset();
        _action = new boolean[Environment.numberOfKeys];
//        Logger.logLine("Number of classifiers: " + _xcs.getPopulationSize() + "/" + _xcs.getPopulationSizeReal());
//        System.out.print(_xcs.getPopulationSize());
        
        float populationRatio = 0.0f; 
        
        if (_xcs.getPopulationSizeReal() != 0.0f)
        {
            populationRatio = _xcs.getPopulationSize() / (float)_xcs.getPopulationSizeReal();
        }

//        System.out.print(" " + winRatio + "\n");
//        _xcs.printClassifiers();
    }

    public void setObservationDetails(final int rfWidth, final int rfHeight, final int egoRow, final int egoCol)
    {
//        XCSObservation.setObservationDetails(rfWidth, rfHeight, egoRow, egoCol);
    }

    public String getName()
    {
        return _name;
    }

    public void setName(String Name)
    {
        this._name = Name;
    }

    public int[] getPreviousCondition()
    {
        return _previousCondition;
    }
    
    public Action getPreviousAction()
    {
        return _previousAction;
    }
    
    public void printPopulation()
    {
        _xcs.printClassifiers();
    }
    
    public void consolidateLearning()
    {
        _xcs.savePopulation();
    }

    // // PROTECTED METHODS ////

    public String conditionToString(int[] condition)
    {
        String ruleString = "[";

        for (int i = 0; i < CONDITION_SIZE; i++)
        {
            ruleString += condition[i];
        }

        ruleString += "]";

        return ruleString;
    }
}